-
Notifications
You must be signed in to change notification settings - Fork 9.8k
[image_picker] Migrate iOS to Pigeon and improve state handling #5285
[image_picker] Migrate iOS to Pigeon and improve state handling #5285
Conversation
Some review notes:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pigeon code looks good. I gave everything a look for clerical errors and didn't notice anything. This is definitely a big improvement. My one big nit is that the plugin is still storing a closure which is error prone and I mention in my comments above. It would be easier to work with and verify as correct if we were instead tying together callbacks and never storing the callback in an instance variable.
UIImagePickerController *imagePickerController = [self createImagePickerController]; | ||
imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext; | ||
imagePickerController.delegate = self; | ||
imagePickerController.mediaTypes = @[ (NSString *)kUTTypeImage ]; | ||
self.callContext = context; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setting this as an instance variable makes it hard to verify that the result is called in all cases. If all the branches to this method had callbacks for their asynchronous operations, we could see that they all correctly call the result back or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that part is still a mess. This is a direct replacement for the existing ivar state (reply
, arguments
), which had the same problem, just now consolidated to one object to make bookkeeping easier.
The reason I decided to explicitly make changing that not in scope (see PR description) is that I wanted to avoid turning the PR into a full rewrite. The pigeon changes are already substantial, but this keeps the actual logic flows mostly the same to limit the scope of what could be broken, and make the review more manageable.
We should absolutely fix that later.
- (void)handleSavedPathList:(NSArray *)pathList { | ||
if (!self.result) { | ||
- (void)returnSavedPathList:(nullable NSArray *)pathList { | ||
if (!self.callContext) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we silently fail in these cases? This seems like a logical error if we get here, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably; this falls under the umbrella of leaving most of the code flows the same. I didn't want to risk introducing a new client-visible error here if there's a latent logic bug in the unchanged overall flow that this has been masking.
Fixing the use of ivars will eliminate the need for this method in the first place, so it'll get fixed then.
return path != null ? PickedFile(path) : null; | ||
} | ||
|
||
Future<String?> _getVideoPath({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: naming is a bit confusing since this says "getVideoPath" but is calling "pickVideo"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's some weird legacy naming in this plugin from the way things were named when the platform interface was created, and then when the new cross-file version were implemented. (This is just a copy from the shared method channel.)
I'll give it a better name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Standardized the helpers as _<host API name>AsPath
. Still not great, but consistent, and it makes it more clear what the distinction is between the helper (returns paths) and the public variants (return XFile
or PickedFile
).
bool operator ==(Object other) { | ||
return other is _LoggedMethodCall && | ||
name == other.name && | ||
mapEquals(arguments, other.arguments); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tangent: Dart equality is kind of a mess. You never know if ==
means identity or value equality. I know there is prior art for mixing these up.
} | ||
} | ||
|
||
class _ApiLogger implements TestHostImagePickerApi { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't you use a mocking library for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Under this testing structure, a mocking library doesn't help; I'm already implementing all of the methods to log them anyway.
If I completely restructured the tests, then yes, but as mentioned in my initial PR comments I deliberately changed the tests as little as I could in this PR in order to ensure that implementations didn't regress something in a way that was masked by test change mistakes.
Pigeon 3.0.2 has been published, so CI is now green. |
Switching iOS owners review to @cyanglaz per new code owner assignments. |
packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m
Show resolved
Hide resolved
*/ | ||
- (void)handleSavedPathList:(NSArray *)pathList { | ||
if (!self.result) { | ||
- (void)returnSavedPathList:(nullable NSArray *)pathList { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nits: The method name suggests it should return the list. Is there a better way to rename it so that it is clearer that this method is sending the result to method channel. (I can't think of any though)
If no better name is found, maybe update the doc so it is more explicit:
"then returns it in callContext.result
of the original method channel call"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed both of the response methods to sendCallResultWith*
, and also updated the comments per your suggestion, so they should be much more explicit about what they are doing.
(Long term, the real solution is to get rid of these methods.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM % nits
Migrates the iOS implementation to a package-internal method channel based on Pigeon.
The driver for adopting pigeon here is that there are shared codepaths used for all three of the native method calls (across async callbacks, with state tracked in ivars), some of which return a single path (
String
) and some of which return multiple paths (List<String>
). Currently making sure the right result type is passed entirely manual—which, as the fixed issue below demonstrates, is error prone. Pigeon allows us to have enforced type safety on the return type, so that it's impossible to accidentally call it the wrong way.Incidental improvements made while updating the ObjC implementation:
Not in scope:
Part of flutter/flutter#94224
Fixes flutter/flutter#100132
Pre-launch Checklist
dart format
.)[shared_preferences]
pubspec.yaml
with an appropriate new version according to the pub versioning philosophy, or this PR is exempt from version changes.CHANGELOG.md
to add a description of the change, following repository CHANGELOG style.///
).